﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using l1cs.server.model.Instance;
using l1cs.server.serverpackets;
using l1cs.server.model;
using l1cs.server.datatables;
using l1cs.server.templates;
using MySql.Data.MySqlClient;
using System.Data;
using l1cs.server.model.skill;
using l1cs.server.utils;

namespace l1cs.server.clientpackets
{
    public class C_LoginToServer : ClientBasePacket
    {
        private const String _C_LOGIN_TO_SERVER = "[C] C_LoginToServer";
        private static Logger _log = Logger.getLogger( typeof( C_LoginToServer ).FullName );

        public C_LoginToServer( byte[] abyte0, ClientThread client )
            : base( abyte0 )
        {
            String login = client.getAccountName();
            String charName = readS();

            if ( client.getActiveChar() != null )
            {
                _log.info( "同一IDでの重複接続の為(" + client.getHostname() + ")との接続を強制切断しました。" );
                client.close();
                return;
            }

            L1PcInstance pc = L1PcInstance.load( charName );
            if ( pc == null || !login.Equals( pc.getAccountName() ) )
            {
                _log.info( "無効なログインリクエスト: char=" + charName + " account=" + login
                        + " host=" + client.getHostname() );
                client.close();
                return;
            }

            if ( Config.LEVEL_DOWN_RANGE != 0 )
            {
                if ( pc.getHighLevel() - pc.getLevel() >= Config.LEVEL_DOWN_RANGE )
                {
                    _log.info( "レベルダウンの許容範囲を超えたキャラクターのログインリクエスト: char="
                            + charName + " account=" + login + " host=" + client.getHostname() );
                    client.kick();
                    return;
                }
            }

            _log.info( String.Format( "キャラクターログイン: char={0} account={1} host={2}",
                                        charName, login, client.getHostname() ) );

            int currentHpAtLoad = pc.getCurrentHp();
            int currentMpAtLoad = pc.getCurrentMp();
            pc.clearSkillMastery();
            pc.setOnlineStatus( 1 );
            CharacterTable.updateOnlineStatus( pc );
            L1World.getInstance().storeObject( pc );

            pc.setNetConnection( client );
            pc.setPacketOutput( client );
            client.setActiveChar( pc );

            S_Unknown1 s_unknown1 = new S_Unknown1();
            pc.sendPackets( s_unknown1 );
            S_Unknown2 s_unknown2 = new S_Unknown2();
            pc.sendPackets( s_unknown2 );

            bookmarks( pc );

            // リスタート先がgetback_restartテーブルで指定されていたら移動させる
            GetBackRestartTable gbrTable = GetBackRestartTable.getInstance();
            L1GetBackRestart gbr = gbrTable.getGetBackRestart( pc.getMapId() );
            if ( gbr != null )
            {
                pc.setX( gbr.getLocX() );
                pc.setY( gbr.getLocY() );
                pc.setMap( gbr.getMapId() );
            }

            // altsettings.propertiesでGetBackがtrueなら街に移動させる
            if ( Config.GET_BACK )
            {
                int[] loc = Getback.GetBack_Location( pc, true );
                pc.setX( loc[0] );
                pc.setY( loc[1] );
                pc.setMap( (short)loc[2] );
            }

            // 戦争中の旗内に居た場合、城主血盟でない場合は帰還させる。
            int castle_id = L1CastleLocation.getCastleIdByArea( pc );
            if ( 0 < castle_id )
            {
                if ( WarTimeController.getInstance().isNowWar( castle_id ) )
                {
                    L1Clan clan = L1World.getInstance().getClan( pc.getClanname() );
                    if ( clan != null )
                    {
                        if ( clan.getCastleId() != castle_id )
                        {
                            // 城主クランではない
                            int[] loc = new int[3];
                            loc = L1CastleLocation.getGetBackLoc( castle_id );
                            pc.setX( loc[0] );
                            pc.setY( loc[1] );
                            pc.setMap( (short)loc[2] );
                        }
                    }
                    else
                    {
                        // クランに所属して居ない場合は帰還
                        int[] loc = new int[3];
                        loc = L1CastleLocation.getGetBackLoc( castle_id );
                        pc.setX( loc[0] );
                        pc.setY( loc[1] );
                        pc.setMap( (short)loc[2] );
                    }
                }
            }

            L1World.getInstance().addVisibleObject( pc );
            // TODO 例外発生して送られないので削除する。
            //S_ActiveSpells s_activespells = new S_ActiveSpells( pc );
            //pc.sendPackets( s_activespells );

            pc.beginGameTimeCarrier();

            S_OwnCharStatus s_owncharstatus = new S_OwnCharStatus( pc );
            pc.sendPackets( s_owncharstatus );

            S_MapID s_mapid = new S_MapID( pc.getMapId(), pc.getMap().isUnderwater() );
            pc.sendPackets( s_mapid );

            S_OwnCharPack s_owncharpack = new S_OwnCharPack( pc );
            pc.sendPackets( s_owncharpack );

            pc.sendPackets( new S_SPMR( pc ) );

            // XXX タイトル情報はS_OwnCharPackに含まれるので多分不要
            S_CharTitle s_charTitle = new S_CharTitle( pc.getId(), pc.getTitle() );
            pc.sendPackets( s_charTitle );
            pc.broadcastPacket( s_charTitle );

            pc.sendVisualEffectAtLogin(); // クラウン、毒、水中等の視覚効果を表示

            pc.sendPackets( new S_Weather( L1World.getInstance().getWeather() ) );

            items( pc );
            skills( pc );
            buff( client, pc );
            pc.turnOnOffLight();

            if ( pc.getCurrentHp() > 0 )
            {
                pc.setDead( false );
                pc.setStatus( 0 );
            }
            else
            {
                pc.setDead( true );
                pc.setStatus( ActionCodes.ACTION_Die );
            }

            if ( pc.getLevel() >= 51 && pc.getLevel() - 50 > pc.getBonusStats() )
            {
                if ( ( pc.getBaseStr() + pc.getBaseDex() + pc.getBaseCon()
                        + pc.getBaseInt() + pc.getBaseWis() + pc.getBaseCha() ) < 210 )
                {
                    pc.sendPackets( new S_bonusstats( pc.getId(), 1 ) );
                }
            }

            if ( Config.CHARACTER_CONFIG_IN_SERVER_SIDE )
            {
                pc.sendPackets( new S_CharacterConfig( pc.getId() ) );
            }

            serchSummon( pc );

            WarTimeController.getInstance().checkCastleWar( pc );

            if ( pc.getClanid() != 0 )
            { // クラン所属中
                L1Clan clan = L1World.getInstance().getClan( pc.getClanname() );
                if ( clan != null )
                {
                    if ( pc.getClanid() == clan.getClanId() && // クランを解散して、再度、同名のクランが創設された時の対策
                            string.Compare( pc.getClanname(), clan.getClanName(), true ) == 0 )
                    {
                        L1PcInstance[] clanMembers = clan.getOnlineClanMember();
                        foreach ( L1PcInstance clanMember in clanMembers )
                        {
                            if ( clanMember.getId() != pc.getId() )
                            {
                                clanMember.sendPackets( new S_ServerMessage( 843, pc
                                        .getName() ) ); // 只今、血盟員の%0%sがゲームに接続しました。
                            }
                        }

                        // 全戦争リストを取得
                        foreach ( L1War war in L1World.getInstance().getWarList() )
                        {
                            bool ret = war.CheckClanInWar( pc.getClanname() );
                            if ( ret )
                            { // 戦争に参加中
                                String enemy_clan_name = war.GetEnemyClanName( pc.getClanname() );
                                if ( enemy_clan_name != null )
                                {
                                    // あなたの血盟が現在_血盟と交戦中です。
                                    pc.sendPackets( new S_War( 8, pc.getClanname(),
                                            enemy_clan_name ) );
                                }
                                break;
                            }
                        }
                    }
                    else
                    {
                        pc.setClanid( 0 );
                        pc.setClanname( "" );
                        pc.setClanRank( 0 );
                        pc.save(); // DBにキャラクター情報を書き込む
                    }
                }
            }

            if ( pc.getPartnerId() != 0 )
            { // 結婚中
                L1PcInstance partner = (L1PcInstance)L1World.getInstance()
                        .findObject( pc.getPartnerId() );
                if ( partner != null && partner.getPartnerId() != 0 )
                {
                    if ( pc.getPartnerId() == partner.getId()
                            && partner.getPartnerId() == pc.getId() )
                    {
                        pc.sendPackets( new S_ServerMessage( 548 ) ); // あなたのパートナーは今ゲーム中です。
                        partner.sendPackets( new S_ServerMessage( 549 ) ); // あなたのパートナーはたった今ログインしました。
                    }
                }
            }

            if ( currentHpAtLoad > pc.getCurrentHp() )
            {
                pc.setCurrentHp( currentHpAtLoad );
            }
            if ( currentMpAtLoad > pc.getCurrentMp() )
            {
                pc.setCurrentMp( currentMpAtLoad );
            }
            pc.startHpRegeneration();
            pc.startMpRegeneration();
            pc.startObjectAutoUpdate();
            client.CharReStart( false );
            pc.beginExpMonitor();
            pc.save(); // DBにキャラクター情報を書き込む

            pc.sendPackets( new S_OwnCharStatus( pc ) );

            if ( pc.getHellTime() > 0 )
            {
                pc.beginHell( false );
            }
        }

        private void items( L1PcInstance pc )
        {
            // DBからキャラクターと倉庫のアイテムを読み込む
            CharacterTable.getInstance().restoreInventory( pc );

            pc.sendPackets( new S_InvList( pc.getInventory().getItems() ) );
        }

        /// <summary>
        /// プレイヤーのブックマークをDBから読み込む。
        /// </summary>
        /// <param name="pc"></param>
        private void bookmarks( L1PcInstance pc )
        {
            string sqlstr = @"SELECT * FROM character_teleport WHERE 
                                char_id = ?1 ORDER BY name ASC";
            using ( MySqlWrapper wrapper = new MySqlWrapper() )
            {
                MySqlCommand cmd = new MySqlCommand( sqlstr );
                cmd.Parameters.Add( "?1", MySqlDbType.Int32 ).Value = pc.getId();
                foreach ( DataRow item in wrapper.GetDataTable( cmd ).Rows )
                {
                    L1BookMark bookmark = new L1BookMark();
                    bookmark.setId( SqlUtils.toInt( item["id"] ) );
                    bookmark.setCharId( SqlUtils.toInt( item["char_id"] ) );
                    bookmark.setName( Convert.ToString( item["name"] ) );
                    bookmark.setLocX( SqlUtils.toInt( item["locx"] ) );
                    bookmark.setLocY( SqlUtils.toInt( item["locy"] ) );
                    bookmark.setMapId( SqlUtils.toShort( item["mapid"] ) );
                    S_Bookmarks s_bookmarks = new S_Bookmarks( bookmark.getName(),
                            bookmark.getMapId(), bookmark.getId() );
                    pc.addBookMark( bookmark );
                    pc.sendPackets( s_bookmarks );
                }
            }
        }

        private void skills( L1PcInstance pc )
        {
            int i = 0;
            int lv1 = 0;
            int lv2 = 0;
            int lv3 = 0;
            int lv4 = 0;
            int lv5 = 0;
            int lv6 = 0;
            int lv7 = 0;
            int lv8 = 0;
            int lv9 = 0;
            int lv10 = 0;
            int lv11 = 0;
            int lv12 = 0;
            int lv13 = 0;
            int lv14 = 0;
            int lv15 = 0;
            int lv16 = 0;
            int lv17 = 0;
            int lv18 = 0;
            int lv19 = 0;
            int lv20 = 0;
            int lv21 = 0;
            int lv22 = 0;
            int lv23 = 0;
            int lv24 = 0;
            int lv25 = 0;
            int lv26 = 0;
            int lv27 = 0;
            int lv28 = 0;
            string sqlstr = "SELECT * FROM character_skills WHERE char_obj_id=?1";
            using ( MySqlWrapper wrapper = new MySqlWrapper() )
            {
                MySqlCommand cmd = new MySqlCommand( sqlstr );
                cmd.Parameters.Add( "?1", MySqlDbType.Int32 ).Value = pc.getId();
                foreach ( DataRow item in wrapper.GetDataTable( cmd ).Rows )
                {
                    int skillId = SqlUtils.toInt( item["skill_id"] );
                    L1Skills l1skills = SkillsTable.getInstance().getTemplate( skillId );
                    if ( l1skills.getSkillLevel() == 1 )
                    {
                        lv1 |= l1skills.getId();
                    }
                    if ( l1skills.getSkillLevel() == 2 )
                    {
                        lv2 |= l1skills.getId();
                    }
                    if ( l1skills.getSkillLevel() == 3 )
                    {
                        lv3 |= l1skills.getId();
                    }
                    if ( l1skills.getSkillLevel() == 4 )
                    {
                        lv4 |= l1skills.getId();
                    }
                    if ( l1skills.getSkillLevel() == 5 )
                    {
                        lv5 |= l1skills.getId();
                    }
                    if ( l1skills.getSkillLevel() == 6 )
                    {
                        lv6 |= l1skills.getId();
                    }
                    if ( l1skills.getSkillLevel() == 7 )
                    {
                        lv7 |= l1skills.getId();
                    }
                    if ( l1skills.getSkillLevel() == 8 )
                    {
                        lv8 |= l1skills.getId();
                    }
                    if ( l1skills.getSkillLevel() == 9 )
                    {
                        lv9 |= l1skills.getId();
                    }
                    if ( l1skills.getSkillLevel() == 10 )
                    {
                        lv10 |= l1skills.getId();
                    }
                    if ( l1skills.getSkillLevel() == 11 )
                    {
                        lv11 |= l1skills.getId();
                    }
                    if ( l1skills.getSkillLevel() == 12 )
                    {
                        lv12 |= l1skills.getId();
                    }
                    if ( l1skills.getSkillLevel() == 13 )
                    {
                        lv13 |= l1skills.getId();
                    }
                    if ( l1skills.getSkillLevel() == 14 )
                    {
                        lv14 |= l1skills.getId();
                    }
                    if ( l1skills.getSkillLevel() == 15 )
                    {
                        lv15 |= l1skills.getId();
                    }
                    if ( l1skills.getSkillLevel() == 16 )
                    {
                        lv16 |= l1skills.getId();
                    }
                    if ( l1skills.getSkillLevel() == 17 )
                    {
                        lv17 |= l1skills.getId();
                    }
                    if ( l1skills.getSkillLevel() == 18 )
                    {
                        lv18 |= l1skills.getId();
                    }
                    if ( l1skills.getSkillLevel() == 19 )
                    {
                        lv19 |= l1skills.getId();
                    }
                    if ( l1skills.getSkillLevel() == 20 )
                    {
                        lv20 |= l1skills.getId();
                    }
                    if ( l1skills.getSkillLevel() == 21 )
                    {
                        lv21 |= l1skills.getId();
                    }
                    if ( l1skills.getSkillLevel() == 22 )
                    {
                        lv22 |= l1skills.getId();
                    }
                    if ( l1skills.getSkillLevel() == 23 )
                    {
                        lv23 |= l1skills.getId();
                    }
                    if ( l1skills.getSkillLevel() == 24 )
                    {
                        lv24 |= l1skills.getId();
                    }
                    if ( l1skills.getSkillLevel() == 25 )
                    {
                        lv25 |= l1skills.getId();
                    }
                    if ( l1skills.getSkillLevel() == 26 )
                    {
                        lv26 |= l1skills.getId();
                    }
                    if ( l1skills.getSkillLevel() == 27 )
                    {
                        lv27 |= l1skills.getId();
                    }
                    if ( l1skills.getSkillLevel() == 28 )
                    {
                        lv28 |= l1skills.getId();
                    }
                    i = lv1 + lv2 + lv3 + lv4 + lv5 + lv6 + lv7 + lv8 + lv9 + lv10
                            + lv11 + lv12 + lv13 + lv14 + lv15 + lv16 + lv17 + lv18
                            + lv19 + lv20 + lv21 + lv22 + lv23 + lv24 + lv25 + lv26 + lv27 + lv28;
                    pc.setSkillMastery( skillId );
                }
                if ( i > 0 )
                {
                    pc.sendPackets( new S_AddSkill( lv1, lv2, lv3, lv4, lv5, lv6,
                            lv7, lv8, lv9, lv10, lv11, lv12, lv13, lv14, lv15,
                            lv16, lv17, lv18, lv19, lv20, lv21, lv22, lv23, lv24, lv25, lv26, lv27, lv28 ) );
                    // _log.warning("ここたち来るのね＠直訳");
                }
            }
        }

        private void serchSummon( L1PcInstance pc )
        {
            foreach ( L1SummonInstance summon in L1World.getInstance().getSummons( pc.getId() ) )
            {
                //if ( summon.getMaster().getId() == pc.getId() )
                //{
                summon.setMaster( pc );
                pc.addPet( summon );
                foreach ( L1PcInstance visiblePc in L1World.getInstance().getVisiblePlayer( summon ) )
                {
                    visiblePc.sendPackets( new S_SummonPack( summon, visiblePc ) );
                }
                //}
            }
        }

        private void buff( ClientThread clientthread, L1PcInstance pc )
        {
            String sqlstr = "SELECT * FROM character_buff WHERE char_obj_id=?1";
            using ( MySqlWrapper wrapper = new MySqlWrapper() )
            {
                MySqlCommand cmd = new MySqlCommand( sqlstr );
                cmd.Parameters.Add( "?1", MySqlDbType.Int32 ).Value = pc.getId();
                foreach ( DataRow item in wrapper.GetDataTable( cmd ).Rows )
                {
                    int skillid = SqlUtils.toInt( item["skill_id"] );
                    int remaining_time = SqlUtils.toInt( item["remaining_time"] );
                    if ( skillid == L1SkillId.SHAPE_CHANGE )
                    { // 変身
                        int poly_id = SqlUtils.toInt( item["poly_id"] );
                        L1PolyMorph.doPoly( pc, poly_id, remaining_time, L1PolyMorph.MORPH_BY_LOGIN );
                    }
                    else if ( skillid == L1SkillId.STATUS_BRAVE )
                    { // ブレイブ ポーション等
                        pc.sendPackets( new S_SkillBrave( pc.getId(), 1, remaining_time ) );
                        pc.broadcastPacket( new S_SkillBrave( pc.getId(), 1, 0 ) );
                        pc.setBraveSpeed( 1 );
                        pc.setSkillEffect( skillid, remaining_time * 1000 );
                    }
                    else if ( skillid == L1SkillId.STATUS_ELFBRAVE )
                    { // エルヴンワッフル
                        pc.sendPackets( new S_SkillBrave( pc.getId(), 3, remaining_time ) );
                        pc.broadcastPacket( new S_SkillBrave( pc.getId(), 3, 0 ) );
                        pc.setBraveSpeed( 1 );
                        pc.setSkillEffect( skillid, remaining_time * 1000 );
                    }
                    else if ( skillid == L1SkillId.STATUS_HASTE )
                    { // グリーン ポーション
                        pc.sendPackets( new S_SkillHaste( pc.getId(), 1, remaining_time ) );
                        pc.broadcastPacket( new S_SkillHaste( pc.getId(), 1, 0 ) );
                        pc.setMoveSpeed( 1 );
                        pc.setSkillEffect( skillid, remaining_time * 1000 );
                    }
                    else if ( skillid == L1SkillId.STATUS_BLUE_POTION )
                    { // ブルーポーション
                        pc.sendPackets( new S_SkillIconGFX( 34, remaining_time ) );
                        pc.setSkillEffect( skillid, remaining_time * 1000 );
                    }
                    else if ( skillid == L1SkillId.STATUS_CHAT_PROHIBITED )
                    { // チャット禁止
                        pc.sendPackets( new S_SkillIconGFX( 36, remaining_time ) );
                        pc.setSkillEffect( skillid, remaining_time * 1000 );
                    }
                    else if ( skillid >= L1SkillId.COOKING_1_0_N && skillid <= L1SkillId.COOKING_1_6_N
                           || skillid >= L1SkillId.COOKING_1_0_S && skillid <= L1SkillId.COOKING_1_6_S
                           || skillid >= L1SkillId.COOKING_2_0_N && skillid <= L1SkillId.COOKING_2_6_N
                           || skillid >= L1SkillId.COOKING_2_0_S && skillid <= L1SkillId.COOKING_2_6_S
                           || skillid >= L1SkillId.COOKING_3_0_N && skillid <= L1SkillId.COOKING_3_6_N
                           || skillid >= L1SkillId.COOKING_3_0_S && skillid <= L1SkillId.COOKING_3_6_S )
                    { // 料理(デザートは除く)
                        L1Cooking.eatCooking( pc, skillid, remaining_time );
                    }
                    else
                    {
                        L1SkillUse l1skilluse = new L1SkillUse();
                        l1skilluse.handleCommands( clientthread.getActiveChar(),
                                skillid, pc.getId(), pc.getX(), pc.getY(), null,
                                remaining_time, L1SkillUse.TYPE_LOGIN );
                    }
                }
            }
        }

        override
        public String getType()
        {
            return _C_LOGIN_TO_SERVER;
        }
    }
}